package com.mavenq.workflow.business.service;

import com.mavenq.workflow.business.domain.PROCINST;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Comment;
import org.camunda.bpm.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Consumer;

@Service
public class flowService {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private IdentityService identityService;
    @Autowired
    private HistoryService historyService;


    /**
     * 创建流程实例
     *
     * @param processDefKey 这里首先需要传入的是流程定义模板的key，前提是我们之前已经部署了相应的流程模板
     * @param businessKey   这是流程实例的业务标识Key，需要用该字段与我们的业务单据进行绑定，该字段可以通过 UUID.randomUUID().toString()来自定生成
     * @param initiator     启动流程实例时的操作人，这里可以理解为制单人，或者是送审人，但是需要注意，在实际应用场景中，我们的制单人不一定就是单据送审人
     * @return
     */
    public PROCINST startProcessInstanceByDefKey(String processDefKey, String businessKey, String initiator) {
        HashMap<String, Object> variable = new HashMap<>();
        if (StringUtils.isNotBlank(initiator)) {
            variable.put("initiator", initiator);
            identityService.setAuthenticatedUserId(initiator); //ACT_HI_PROCINST.START_USER_ID字段的赋值-开始节点人
        }
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(processDefKey, businessKey, variable);
        PROCINST procinst = new PROCINST(instance.getProcessDefinitionId(), instance.getProcessInstanceId(), instance.getBusinessKey(), instance.isSuspended(), instance.isEnded());
        return procinst;
    }

    /**
     * 送审/审核操作
     * @param businessKey
     * @param initiator
     * @param comment
     * @return
     */
    public String submitProcessInstance(String businessKey, String initiator, String comment) {
        String resultString = "";
        Task task = queryTaskByBusinessKey(businessKey, initiator);
        if (Objects.isNull(task)) {
            resultString = "没有查询到对应的单据流程";
        } else if (!task.getAssignee().equalsIgnoreCase(initiator)) {
            resultString = "没有审核权限！";
        } else {
            String taskId = task.getId();
            System.out.println("taskId = " + taskId);
            Comment comment1 = taskService.createComment(taskId, task.getProcessInstanceId(), comment);
            System.out.println("comment1 = " + comment1);
            taskService.complete(taskId);
            resultString = "审核成功！";
        }
        return resultString;
    }

    /**
     * 根据业务标识代码获取当前节点
     */
    private Task queryTaskByBusinessKey(String businessKey, String initiator) {
        Task task = null;
        ProcessInstance instance = runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        if (Objects.isNull(instance)) return null;
        List<Task> list = taskService.createTaskQuery()
                .processInstanceId(instance.getProcessInstanceId())
                .active() //正在运行时的节点？
                .list();
        if (list.size() == 1) task = list.get(0);
        else {
            task = taskService.createTaskQuery()
                    .processInstanceId(instance.getProcessInstanceId())
                    .active()
                    .taskAssignee(initiator)
                    .singleResult();
        }
        return task;
    }

    //查询我创建的流程
    public List<String> queryMyProcess(String initiator) {
        /* 迭代【可添加更多种条件查询】:时间范围、内置分页查询等！ */
        List<HistoricProcessInstance> list  = historyService.createHistoricProcessInstanceQuery()
                //通过制单人来查询流程中的数据
                .startedBy(initiator)
                .list();
        if (CollectionUtils.isEmpty(list)){
            return null;
        }
        ArrayList<String> businessKeyList = new ArrayList<>();
        list.forEach(procInst -> {
            businessKeyList.add(procInst.getBusinessKey());
        });
        return businessKeyList;
    }


    /**
     * 查询已办/未办 单据
     * */
    public List<String> queryMyTodoTask(String userId, String type) {
        ArrayList<String> businessList = new ArrayList<>();
        //查询代办
        if (type.equalsIgnoreCase("0")) {
            List<Task> tasks = taskService.createTaskQuery()
                    .taskAssignee(userId)
                    .active()
//                    .listPage() 【可分页查询】
                    .list();
            if (CollectionUtils.isEmpty(tasks)){
                return null;
            }
            tasks.forEach(task -> {
                //为查询到相关单据
                String businessKey = historyService.createHistoricProcessInstanceQuery()
//                        .orderByProcessInstanceStartTime() 【可添加时间查询范围】
//                        .orderByProcessInstanceEndTime()
                        .processInstanceId(task.getProcessInstanceId())
                        .singleResult()
                        .getBusinessKey();
                businessList.add(businessKey);
            });
        }
        //查询已办
        else if (type.equalsIgnoreCase("1")) {
            List<HistoricTaskInstance> completedTaskList = historyService.createHistoricTaskInstanceQuery()
                    .taskAssignee(userId)
                    .finished()
                    .taskDeleteReason("completed")
                    .list();
            if (CollectionUtils.isEmpty(completedTaskList)){
                //为查询到相关单据
                return null;
            }
            completedTaskList.forEach(taskOld ->{
                String businessKey = historyService.createHistoricProcessInstanceQuery()
//                    .orderByProcessInstanceStartTime()  【可添加时间查询范围】
//                    .orderByProcessInstanceEndTime()
                        .processInstanceId(taskOld.getProcessInstanceId())
                        .singleResult()
                        .getBusinessKey();
                businessList.add(businessKey);
            });
        }
        return businessList;
    }


    /**
     *驳回操作
     * */
    public String turnDownTask(String userId, String businessKey, String type, String comment) {
        Task task = queryTaskByBusinessKey(businessKey, userId);
        if (Objects.isNull(task)){
            String result = new String("未查询到对应的审核单据！");
            return new String("未查询到对应的审核单据！");
        }
        if (!task.getAssignee().equalsIgnoreCase(userId)){
            return new String("没用审核权限！");
        }
        //获取所有已办节点
        List<HistoricActivityInstance> userTaskList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                //用户类型节点
                .activityType("userTask")
                .finished() //已经完成的节点
                .orderByHistoricActivityInstanceEndTime()
                .asc()
                .list();
        //流程实例的活动实例树
        /* ActivityInstance tree = runtimeService.getActivityInstance(task.getProcessInstanceId()); */

        if (userTaskList == null || CollectionUtils.isEmpty(userTaskList)){
            return new String("当前任务无法驳回！");
        }
        switch (type){
            //驳回第一任制单人
            case "1":{
                // TODO: 2023-10-30  1.驳回提交已经无  ok2.驳回第一任还是驳回的上一任  3.会签节点上的驳回操作出现只结束了当前任务的驳回！
                if (userTaskList.size()<2){
                    return  new String("第一个用户节点无法驳回！");
                }
                HistoricActivityInstance historicActivityInstance = userTaskList.get(0);
                String toActId = historicActivityInstance.getActivityId();
                String assignee = historicActivityInstance.getAssignee();
                //设置流程可变参数
                HashMap<String, Object> taskVariable = new HashMap<>();
                taskVariable.put("assignee",assignee);
                //流程审核+驳回
                //任务流程创建了提交模板Comment 但是没有提交 taskService.complete(taskId)。--所有节点也不会提交到后台去
                taskService.createComment(task.getId(),task.getProcessInstanceId(),"驳回原因："+comment);
                //任务流程实例修改位置
                runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
//                        .cancelActivityInstance(getInstanceIdForActivity(tree,task.getTaskDefinitionKey())) //关闭当前节点相关的任务
                        .cancelAllForActivity(task.getTaskDefinitionKey())
                        //暂时不清楚该内容提交到何处！
                        .setAnnotation("进行了驳回到第一任务节点操作！")
                        //启动目标活动节点
                        .startBeforeActivity(toActId)
                        .setVariables(taskVariable)
                        .execute();
                return new String("驳回到制单人成功！");
            }
            //驳回上一任
            case "2":{
                //判断当前节点是否为第一个节点
                HistoricActivityInstance historicActivityInstance = userTaskList.get(0);
                String activityId = historicActivityInstance.getActivityId();
                if(activityId.equals(task.getTaskDefinitionKey())) {
                    return new String("第一节点无法驳回！");
                }
                //获取上一个节点
                Map<String, String> lastNode = getLastNode(userTaskList, task.getTaskDefinitionKey());
                if (Objects.isNull(lastNode)) {
                    return new String("退回节点异常！");
                }
                String toActId = lastNode.get("toActId");
                String assignee = lastNode.get("assignee");
                //设置流程中的可变参数
                HashMap<String, Object> taskVariable = new HashMap<>(2);
                taskVariable.put("user",assignee);
                //进行驳回操作
                taskService.createComment(task.getId(),task.getProcessInstanceId(),"驳回："+comment);
                runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
                        //.cancelActivityInstance(getInstanceIdForActivity(tree,task.getTaskDefinitionKey())) //关闭相关任务！(当前节点就会被删除。但是之前审核过的节点还是会存在！)
                        //该方式关闭所有activityId相同的activity活动都会被取消暂停（会签节点）
                        .cancelAllForActivity(task.getTaskDefinitionKey())
                        .setAnnotation("进行驳回到上一任务节点操作！")
                        //启动目标活动节点
                        .startBeforeActivity(toActId)
                        //流程可变参数赋值
                        .setVariables(taskVariable)
                        .execute();
                return new String("驳回上一任成功！");
            }
            //驳回任一任
            case "3":{
                //
            }
            default:{
            }
        }
        return null;
    }

    private Map<String,String> getLastNode(List<HistoricActivityInstance> resultList,String currentActivityId){
        HashMap<String, String> backNode = new HashMap<>();
        //新建一个有序不重复集合
        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
        for (HistoricActivityInstance his: resultList){
            linkedHashMap.put(his.getActivityId(),his.getAssignee());
        }
        int originSize = resultList.size();
        //判断历史节点中是否已经存在过当前节点
        boolean flag = false;
        for (Map.Entry entry:linkedHashMap.entrySet()){
            if (currentActivityId.equalsIgnoreCase((String) entry.getKey())){
                flag = true;
                break;
            }
        }
        //当前节点不在历史节点里面，最后一个节点是完成节点
        if (!flag){
            HistoricActivityInstance historicActivityInstance = resultList.get(originSize - 1);
            backNode.put("toActId",historicActivityInstance.getActivityId());
            backNode.put("assignee",historicActivityInstance.getAssignee());
            return backNode;
            //当前节点在历史节点中（已经退回过）
        }else {
            ListIterator<Map.Entry<String, String>> li = new ArrayList<>(linkedHashMap.entrySet()).listIterator();
            while (li.hasNext()){
                Map.Entry<String, String> entry = li.next();
                if (currentActivityId.equalsIgnoreCase(entry.getKey())){
                    //光标上一到当前节点
                    li.previous();
                    //当前相同节点的上一节点
                    Map.Entry<String, String> previous = li.previous();
                    backNode.put("toActId",previous.getKey());
                    backNode.put("assignee",previous.getValue());
                    return backNode;
                }
            }
        }
        return null;
    }


    /**
     *@Description--节点的销审操作！
     *@Param [businessKey, userId, comment]
     *@return java.lang.String
     */
    public String revocationTask(String businessKey, String userId, String comment) {

        //查询当前运行时节点
        ProcessInstance instance = runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        if (Objects.isNull(instance)) {
            return "未查询到相关单据信息！";
        }
        List<Task> list = taskService.createTaskQuery()
                .processInstanceId(instance.getProcessInstanceId())
                .active() //正在运行时的节点？
                .list();
        Task task = list.get(0);

        //查询历史审核节点
        LinkedHashMap<String,Map> taskList =(LinkedHashMap<String, Map>) queryOldTaskList(businessKey);
        if (Objects.isNull(taskList)) {
            return "没有销审权限！";
        }
        int size = taskList.size();
        //todo : size-1????
        String taskListKey =(String) taskList.keySet().toArray()[size-1];
        Map map = taskList.get(taskListKey);
        String act_assignee =(String) map.get("act_Assignee");
        String act_id = (String) map.get("act_Id");
        if (! act_assignee.equalsIgnoreCase(userId)) {
            return "您没有权限销审当前单据！";
        }else {
            taskService.createComment(task.getId(),task.getProcessInstanceId(), "销审：" + comment);
            HashMap<String,Object> taskVariable = new HashMap<>();
            taskVariable.put("assignee",act_assignee);
            runtimeService.createProcessInstanceModification(task.getProcessInstanceId())
                    .cancelAllForActivity(task.getTaskDefinitionKey())
                    .setAnnotation("销审回撤到上一节点！")
                    .startBeforeActivity(act_id)
                    .setVariables(taskVariable)
                    .execute();
        }
        return "销审成功！";
    }


    /** 查询已经审核过的节点[1.驳回任一节点时使用  2.被驳回后的送审操作*/
    public Object queryOldTaskList(String businessKey) {
        HistoricProcessInstance historicProcessInstance = queryProcessInstanceByBusinessKey(businessKey);
        if (Objects.isNull(historicProcessInstance)) {
            return "未查询到相关单据！";}
        List<HistoricActivityInstance> userTask = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(historicProcessInstance.getId())
                .activityType("userTask")
                .finished()
                .orderByHistoricActivityInstanceEndTime()
                .asc()
                .list();
        LinkedHashMap<String,Map> taskLink = new LinkedHashMap<>();
        userTask.stream().forEach(new Consumer<HistoricActivityInstance>(){
            @Override
            public void accept(HistoricActivityInstance historicActivityInstance) {
                String activityName = historicActivityInstance.getActivityName();
                if (taskLink.containsKey(activityName)){
                    return;
                }
                else {
                    HashMap<String, String> map = new HashMap<>();
                    map.put("act_Id",historicActivityInstance.getActivityId());
                    map.put("act_Assignee",historicActivityInstance.getAssignee());
                    taskLink.put(historicActivityInstance.getActivityName(),map);
                }
            }
        });
        return taskLink;
    }

    private HistoricProcessInstance queryProcessInstanceByBusinessKey(String businessKey) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        return historicProcessInstance;
    }


    //审核日志查询
    /**
     * [注:日志顺序  1开始时间 相同顺延 2排列结束时间]
     * activityType：节点类型 null就不显示
     * taskId：taskId相同的为会签节点
     * state：completed审核完成  deleted驳回  null待审核
     * */
    public List queryProcessLog(String businessKey) {
        String processInstanceId = historyService.createHistoricProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .singleResult()
                .getRootProcessInstanceId();
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceStartTime() //这里开始时间相同的可以，只能前端在根据结束时间继续排序
                .asc()
                .list();
        List<Map<String,Object>> result=new ArrayList<>(list.size());
        System.out.println(list.size());
        for (HistoricActivityInstance historicActivityInstance : list) {
            Map<String,Object> map=new HashMap<>();
            String taskId = historicActivityInstance.getTaskId();
            List<Comment> taskComments = taskService.getTaskComments(taskId);
            System.out.println("taskId = " + taskId);
            System.out.println(taskComments.size());
            map.put("activityName",historicActivityInstance.getActivityName());
            System.out.println("historicActivityInstance.getActivityType() = " + historicActivityInstance.getActivityType());
            map.put("activityType",matching(historicActivityInstance.getActivityType()));
            map.put("assignee",historicActivityInstance.getAssignee()==null?"无":historicActivityInstance.getAssignee());
            map.put("taskId",historicActivityInstance.getTaskId());
            map.put("act_Id",historicActivityInstance.getActivityId());
            /*加入activity状态字段*/
            if (taskId != null) {
                map.put("state",historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult().getDeleteReason());
            }
            Date startTime = historicActivityInstance.getStartTime();
            if (Objects.isNull(startTime)){
                map.put("starTime","");
            }
            else{
                map.put("startTime", DateFormatUtils.format(historicActivityInstance.getStartTime(),"yyyy-MM-dd HH:mm:ss") );
            }
            Date endTime = historicActivityInstance.getEndTime();
            if (Objects.isNull(endTime)){
                map.put("endTime","");
                map.put("costTime","");
            }else {
                map.put("endTime", DateFormatUtils.format(historicActivityInstance.getEndTime(),"yyyy-MM-dd HH:mm:ss"));
                map.put("costTime",getDatePoor(historicActivityInstance.getEndTime(),historicActivityInstance.getStartTime()));

            }
            if (taskComments.size()>0){
                map.put("message",taskComments.get(0).getFullMessage());
            }else {
                map.put("message","无");
            }
            result.add(map);
        }
        System.out.println("result = " + result);
        return result;
    }

    /** 日志log类型替换 */
    private String matching(String ActivityType){
        String value="";
        switch (ActivityType){
            case "startEvent":
                value="流程开始";
                break;
            case "userTask":
                value="用户处理";
                break;
            case "noneEndEvent":
                value="流程结束";
                break;
            default:
                value="未知节点";
                break;
        }
        return value;
    }

    /** 时间差计算 */
    public  String getDatePoor(Date endDate, Date nowDate) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - nowDate.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟"+ sec + "秒";
    }




}
